#pragma once
#include "SehnsuchtPrecompiled.hxx"

namespace Sehnsucht
{
	// A helper class that provides convenient functions for creating common geometrical shapes.
	class BasicShapes
	{

#if _Reign_desktop_support_windows_7
		typedef Microsoft::WRL::ComPtr<ID3D11Device> Device3;
		typedef Device3::InterfaceType *Device3Ptr;
#else
		typedef Microsoft::WRL::ComPtr<ID3D11Device1> Device3;
		typedef Device3::InterfaceType *Device3Ptr;
#endif
		typedef Microsoft::WRL::ComPtr<ID3D11Buffer> Buffer;
		typedef Buffer::InterfaceType **BufferAddress;
		typedef Buffer::InterfaceType *BufferPtr;

	public:

		enum class ProvidedData
		{
			PositionLocal = BitMask(0),
			NormalLocal = BitMask(1),
			TexCoord = BitMask(2),
			All = PositionLocal | NormalLocal | TexCoord
		};

		// Vertex type to fill the data in.
		template <class TVertex >
		// This type allow this to class to work with generic vertex types.
		class IVertexDataProvider 
		{
		public:

			// Creates new vertex. Can be used for raw structures and classes w/o virtual functionality. Otherwise, override.
			virtual TVertex NewVertex() { TVertex vertex; return vertex; }

			// Zeroes existing vertex. Can be used for raw structures and classes w/o virtual functionality. Otherwise, override.
			virtual void ClearVertex(_Inout_ TVertex *targetVertex) { ZeroValue(targetVertex); }

			// Copies existing vertex. Can be used for structures and classes w/o dynamically allocated memory. Otherwise, override.
			virtual TVertex CopyVertex(_Inout_ TVertex targetVertex) { return targetVertex; }

			// Programmer should specify the way he wants the data to be filled into the vertex. Has no default implementation.
			virtual void FillVertex(
				_In_ Reign::Vector3 const &positionLocal, // Vertex position in the model space.
				_In_ Reign::Vector3 const &normalLocal, // Vertex normal in the model space.
				_In_ Reign::Vector2 const &texCoord, // Vertex texture coordinates.
				_In_ ProvidedData providedDataMask, // Specifies what data is actually provided.
				_Inout_ TVertex *targetVertex // Vertex to fill the data in.
				) pure;
		};

	public:

		template <class TVertex, typename TIndex = uint16>
		static void CreateBox(
			_In_ IVertexDataProvider<TVertex> *converter,
			_In_ Reign::Vector3 r,
			_Inout_ std::vector<TVertex> &vertices,
			_Inout_ std::vector<TIndex> &indices
			)
		{
			typedef Reign::Vector3 float3;
			typedef Reign::Vector2 float2;

			auto vertex = converter->NewVertex();

			// FLOOR
			converter->FillVertex(float3(-r.x, -r.y, r.z), float3(0.0f, 1.0f, 0.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, -r.y, r.z), float3(0.0f, 1.0f, 0.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, -r.y, -r.z), float3(0.0f, 1.0f, 0.0f), float2(0.0f, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, -r.y, -r.z), float3(0.0f, 1.0f, 0.0f), float2(1.0f, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			// WALL
			converter->FillVertex(float3(-r.x, r.y, r.z), float3(0.0f, 0.0f, -1.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, r.y, r.z), float3(0.0f, 0.0f, -1.0f), float2(2.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, -r.y, r.z), float3(0.0f, 0.0f, -1.0f), float2(0.0f, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, -r.y, r.z), float3(0.0f, 0.0f, -1.0f), float2(2.0f, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			// WALL
			converter->FillVertex(float3(r.x, r.y, r.z), float3(-1.0f, 0.0f, 0.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, r.y, -r.z), float3(-1.0f, 0.0f, 0.0f), float2(r.y, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, -r.y, r.z), float3(-1.0f, 0.0f, 0.0f), float2(0.0f, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, -r.y, -r.z), float3(-1.0f, 0.0f, 0.0f), float2(r.y, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			// WALL
			converter->FillVertex(float3(r.x, r.y, -r.z), float3(0.0f, 0.0f, 1.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, r.y, -r.z), float3(0.0f, 0.0f, 1.0f), float2(2.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, -r.y, -r.z), float3(0.0f, 0.0f, 1.0f), float2(0.0f, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, -r.y, -r.z), float3(0.0f, 0.0f, 1.0f), float2(2.0f, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			// WALL
			converter->FillVertex(float3(-r.x, r.y, -r.z), float3(1.0f, 0.0f, 0.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, r.y, r.z), float3(1.0f, 0.0f, 0.0f), float2(r.y, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, -r.y, -r.z), float3(1.0f, 0.0f, 0.0f), float2(0.0f, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, -r.y, r.z), float3(1.0f, 0.0f, 0.0f), float2(r.y, 1.5f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			// CEILING
			converter->FillVertex(float3(-r.x, r.y, -r.z), float3(0.0f, -1.0f, 0.0f), float2(-0.15f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, r.y, -r.z), float3(0.0f, -1.0f, 0.0f), float2(1.25f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, r.y, r.z), float3(0.0f, -1.0f, 0.0f), float2(-0.15f, 2.1f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, r.y, r.z), float3(0.0f, -1.0f, 0.0f), float2(1.25f, 2.1f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			{
				TIndex boxIndices [] =
				{
					0, 2, 1,
					1, 2, 3,

					4, 6, 5,
					5, 6, 7,

					8, 10, 9,
					9, 10, 11,

					12, 14, 13,
					13, 14, 15,

					16, 18, 17,
					17, 18, 19,

					20, 22, 21,
					21, 22, 23,
				};

				for (auto index : boxIndices)
				{
					indices.push_back(index);
				}
			}
		}

		template <class TVertex, typename TIndex = uint16>
		static void CreateCube(
			_In_ IVertexDataProvider<TVertex> *converter,
			_Inout_ std::vector<TVertex> &vertices,
			_Inout_ std::vector<TIndex> &indices
			)
		{
			typedef Reign::Vector3 float3;
			typedef Reign::Vector2 float2;

			auto vertex = converter->NewVertex();

			converter->FillVertex(float3(-0.5f, 0.5f, -0.5f), float3(0.0f, 1.0f, 0.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex); // +Y (top face)
			converter->FillVertex(float3(0.5f, 0.5f, -0.5f), float3(0.0f, 1.0f, 0.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(0.5f, 0.5f, 0.5f), float3(0.0f, 1.0f, 0.0f), float2(1.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-0.5f, 0.5f, 0.5f), float3(0.0f, 1.0f, 0.0f), float2(0.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			converter->FillVertex(float3(-0.5f, -0.5f, 0.5f), float3(0.0f, -1.0f, 0.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex); // -Y (bottom face)
			converter->FillVertex(float3(0.5f, -0.5f, 0.5f), float3(0.0f, -1.0f, 0.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(0.5f, -0.5f, -0.5f), float3(0.0f, -1.0f, 0.0f), float2(1.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-0.5f, -0.5f, -0.5f), float3(0.0f, -1.0f, 0.0f), float2(0.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			converter->FillVertex(float3(0.5f, 0.5f, 0.5f), float3(1.0f, 0.0f, 0.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex); // +X (right face)
			converter->FillVertex(float3(0.5f, 0.5f, -0.5f), float3(1.0f, 0.0f, 0.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(0.5f, -0.5f, -0.5f), float3(1.0f, 0.0f, 0.0f), float2(1.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(0.5f, -0.5f, 0.5f), float3(1.0f, 0.0f, 0.0f), float2(0.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			converter->FillVertex(float3(-0.5f, 0.5f, -0.5f), float3(-1.0f, 0.0f, 0.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex); // -X (left face)
			converter->FillVertex(float3(-0.5f, 0.5f, 0.5f), float3(-1.0f, 0.0f, 0.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-0.5f, -0.5f, 0.5f), float3(-1.0f, 0.0f, 0.0f), float2(1.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-0.5f, -0.5f, -0.5f), float3(-1.0f, 0.0f, 0.0f), float2(0.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			converter->FillVertex(float3(-0.5f, 0.5f, 0.5f), float3(0.0f, 0.0f, 1.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex); // +Z (front face)
			converter->FillVertex(float3(0.5f, 0.5f, 0.5f), float3(0.0f, 0.0f, 1.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(0.5f, -0.5f, 0.5f), float3(0.0f, 0.0f, 1.0f), float2(1.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-0.5f, -0.5f, 0.5f), float3(0.0f, 0.0f, 1.0f), float2(0.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			converter->FillVertex(float3(0.5f, 0.5f, -0.5f), float3(0.0f, 0.0f, -1.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex); // -Z (back face)
			converter->FillVertex(float3(-0.5f, 0.5f, -0.5f), float3(0.0f, 0.0f, -1.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-0.5f, -0.5f, -0.5f), float3(0.0f, 0.0f, -1.0f), float2(1.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(0.5f, -0.5f, -0.5f), float3(0.0f, 0.0f, -1.0f), float2(0.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			{
				TIndex cubeIndices [] =
				{
					0, 1, 2,
					0, 2, 3,

					4, 5, 6,
					4, 6, 7,

					8, 9, 10,
					8, 10, 11,

					12, 13, 14,
					12, 14, 15,

					16, 17, 18,
					16, 18, 19,

					20, 21, 22,
					20, 22, 23
				};

				for (auto index : cubeIndices)
				{
					indices.push_back(index);
				}
			}
		}

		template <class TVertex, typename TIndex = uint16>
		static void CreateVerticalRect(
			_In_ IVertexDataProvider<TVertex> *converter,
			_In_ Reign::Vector2 r,
			_Inout_ std::vector<TVertex> &vertices,
			_Inout_ std::vector<TIndex> &indices
			)
		{
			typedef Reign::Vector3 float3;
			typedef Reign::Vector2 float2;

			auto vertex = converter->NewVertex();

			converter->FillVertex(float3(-r.x, r.y, 0.0f), float3(0.0f, 0.0f, 1.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); 
			vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, r.y, 0.0f), float3(0.0f, 0.0f, 1.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); 
			vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, -r.y, 0.0f), float3(0.0f, 0.0f, 1.0f), float2(0.0f, 1.0f), ProvidedData::All, &vertex); 
			vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, -r.y, 0.0f), float3(0.0f, 0.0f, 1.0f), float2(1.0f, 1.0f), ProvidedData::All, &vertex); 
			vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			{
				TIndex cubeIndices [] =
				{
					2, 1, 0,
					1, 2, 3
				};

				for (auto index : cubeIndices)
				{
					indices.push_back(index);
				}
			}
		}

		template <class TVertex, typename TIndex = uint16>
		static void CreateHorizonalRect(
			_In_ IVertexDataProvider<TVertex> *converter,
			_In_ Reign::Vector2 r,
			_Inout_ std::vector<TVertex> &vertices,
			_Inout_ std::vector<TIndex> &indices
			)
		{
			typedef Reign::Vector3 float3;
			typedef Reign::Vector2 float2;

			auto vertex = converter->NewVertex();

			converter->FillVertex(float3(-r.x, 0.0f, -r.y), float3(0.0f, -1.0f, 0.0f), float2(0.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, 0.0f, -r.y), float3(0.0f, -1.0f, 0.0f), float2(1.0f, 0.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(-r.x, 0.0f, r.y), float3(0.0f, -1.0f, 0.0f), float2(0.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);
			converter->FillVertex(float3(r.x, 0.0f, r.y), float3(0.0f, -1.0f, 0.0f), float2(1.0f, 1.0f), ProvidedData::All, &vertex); vertices.push_back(converter->CopyVertex(vertex)); converter->ClearVertex(&vertex);

			{
				TIndex cubeIndices [] =
				{
					0, 1, 2,
					3, 2, 1
				};

				for (auto index : cubeIndices)
				{
					indices.push_back(index);
				}
			}
		}

		template <class TVertex, typename TIndex = uint16>
		static void CreateSphere(
			_In_ IVertexDataProvider<TVertex> *converter,
			_Inout_ std::vector<TVertex> &vertices,
			_Inout_ std::vector<TIndex> &indices
			)
		{
			typedef Reign::Vector3 float3;
			typedef Reign::Vector2 float2;

			const int numSegments = 64;
			const int numSlices = numSegments / 2;
			const int numVertices = (numSlices + 1) * (numSegments + 1);

			auto vertex = converter->NewVertex();
			{
				TVertex *sphereVertices = new TVertex[numVertices];

				for (int slice = 0; slice <= numSlices; slice++)
				{
					const float v = (float) slice / (float) numSlices;
					const float inclination = v * PI_F;
					const float y = cos(inclination);
					const float r = sin(inclination);

					for (int segment = 0; segment <= numSegments; segment++)
					{
						const float u = (float) segment / (float) numSegments;
						const float azimuth = u * PI_F * 2.0f;
						const int index = slice * (numSegments + 1) + segment;
						const auto pos = float3(r * sin(azimuth), y, r * cos(azimuth));

						converter->FillVertex(pos, pos, float2(u, v), ProvidedData::All, &vertex);
						sphereVertices[index] = converter->CopyVertex(vertex);
					}
				}

				for (auto i = 0; i < numVertices; i++)
				{
					vertices.push_back(sphereVertices[i]);
				}

				Safe_DeleteArray(sphereVertices);
			}

			{
				const int numIndices = numSlices * (numSegments - 2) * 6;
				TIndex [] sphereIndices = new unsigned short[numIndices];

				unsigned int index = 0;
				for (int slice = 0; slice < numSlices; slice++)
				{
					const TIndex sliceBase0 = (TIndex) ((slice)*(numSegments + 1));
					const TIndex sliceBase1 = (TIndex) ((slice + 1)*(numSegments + 1));
					for (int segment = 0; segment < numSegments; segment++)
					{
						if (slice > 0)
						{
							sphereIndices[index++] = sliceBase0 + segment;
							sphereIndices[index++] = sliceBase0 + segment + 1;
							sphereIndices[index++] = sliceBase1 + segment + 1;
						}
						if (slice < numSlices - 1)
						{
							sphereIndices[index++] = sliceBase0 + segment;
							sphereIndices[index++] = sliceBase1 + segment + 1;
							sphereIndices[index++] = sliceBase1 + segment;
						}
					}
				}

				for (auto i = 0; i < numIndices; i++)
				{
					indices.push_back(sphereIndices[i]);
				}

				Safe_DeleteArray(sphereIndices);
			}
		}

		template <class TVertex>
		static void CreateVertexBuffer(
			_In_ Device3Ptr d3dDevice,
			_In_ uint32 numVertices,
			_In_ TVertex *vertexData,
			_Out_ BufferAddress vertexBuffer
			)
		{
			(*vertexBuffer) = nullptr;
			Buffer vertexBufferInternal;

			D3D11_BUFFER_DESC vertexBufferDesc;
			vertexBufferDesc.ByteWidth = numVertices * sizeof TVertex;
			vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
			vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
			vertexBufferDesc.CPUAccessFlags = 0;
			vertexBufferDesc.MiscFlags = 0;
			vertexBufferDesc.StructureByteStride = 0;

			D3D11_SUBRESOURCE_DATA vertexBufferData;
			vertexBufferData.pSysMem = vertexData;
			vertexBufferData.SysMemPitch = 0;
			vertexBufferData.SysMemSlicePitch = 0;

			Sehnsucht::ThrowIfFailed(
				d3dDevice->CreateBuffer(
					&vertexBufferDesc,
					&vertexBufferData,
					&vertexBufferInternal
					)
				);

			(*vertexBuffer) = vertexBufferInternal.Detach();
		}

		template <typename TIndex = uint16>
		static void CreateIndexBuffer(
			_In_ Device3Ptr d3dDevice,
			_In_ uint32 numIndices,
			_In_ TIndex *indexData,
			_Out_ BufferAddress indexBuffer
			)
		{
			(*indexBuffer) = nullptr;
			Buffer indexBufferInternal;

			D3D11_BUFFER_DESC indexBufferDesc;
			indexBufferDesc.ByteWidth = numIndices * sizeof TIndex;
			indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
			indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
			indexBufferDesc.CPUAccessFlags = 0;
			indexBufferDesc.MiscFlags = 0;
			indexBufferDesc.StructureByteStride = 0;

			D3D11_SUBRESOURCE_DATA indexBufferData;
			indexBufferData.pSysMem = indexData;
			indexBufferData.SysMemPitch = 0;
			indexBufferData.SysMemSlicePitch = 0;

			Sehnsucht::ThrowIfFailed(
				d3dDevice->CreateBuffer(
					&indexBufferDesc,
					&indexBufferData,
					&indexBufferInternal
					)
				);

			(*indexBuffer) = indexBufferInternal.Detach();
			indexBufferInternal = nullptr;
		}
	};

	inline void operator|=(BasicShapes::ProvidedData &thisValue, BasicShapes::ProvidedData const &otherValue)
	{
		thisValue = (BasicShapes::ProvidedData)(_To_uint thisValue | _To_uint otherValue);
	};

	inline BasicShapes::ProvidedData operator|(BasicShapes::ProvidedData const &thisValue, BasicShapes::ProvidedData const &otherValue)
	{
		return (BasicShapes::ProvidedData)(_To_uint thisValue | _To_uint otherValue);
	};

	inline BasicShapes::ProvidedData operator&(BasicShapes::ProvidedData const &thisValue, BasicShapes::ProvidedData const &otherValue)
	{
		return (BasicShapes::ProvidedData)(_To_uint thisValue & _To_uint otherValue);
	};
};